#include "proxycore/base/basedefines.h"
#include "proxycore/parse/httpparser.h"

namespace proxy{

  int CaseStrncmp(const char*a, const char*b, int size){
    for (int i = 0; i < size && a[i] && b[i]; i++){
      if (std::tolower(a[i]) != std::tolower(b[i]))
        return a[i] - b[i];
    }
    return 0;
  }

  void CopyUrlToHttpUrl(proxy::HttpUrl *http_url,
    const char *url, int size, const struct http_parser_url *u){
    if (u->field_set & UF_HOST){
      http_url->host.append(
        url + u->field_data[UF_HOST].off, u->field_data[UF_HOST].len);
      std::cout << "http_url->host \t" << http_url->host << std::endl;
    }
    if (u->field_set & UF_PORT){
      http_url->port.append(
        url + u->field_data[UF_PORT].off, u->field_data[UF_PORT].len);
      std::cout << "http_url->port \t" << http_url->port << std::endl;
    }
    if (u->field_set & UF_PATH){
      http_url->path.append(
        url + u->field_data[UF_PATH].off, size - u->field_data[UF_PATH].off);
      std::cout << "http_url->path \t" << http_url->path << std::endl;
    }
  }

  void AnysisConnectUrl(proxy::HttpUrl *http_url, const char *url, int size){
    int pos = -1;
    for (int i = 0; i < size; i++){
      if (url[i] == ':'){
        pos = i;
      }
    }
    http_url->host.append(url, pos);
    if (pos != -1){
      http_url->port.append(url + pos + 1, size - pos);
    }
    else{
      http_url->port = "443";
    }
    std::cout << "\t\t" << http_url->host << " : " << http_url->port << std::endl;
  }

  // http_cb
  int OnMessageBegin(http_parser* parser){
    std::cout << __FUNCTION__ << std::endl;
    return 0;
  }

  // http_data_cb
  int OnUrl(http_parser* parser, const char *at, size_t length){
    proxy::HttpRequest *http_request = (proxy::HttpRequest *)parser->data;
    std::cout << __FUNCTION__ << "\t\t" << http_method_str((http_method)parser->method) << "\t";
    std::cout.write(at, length) << std::endl;
    if (parser->method == HTTP_CONNECT){
      AnysisConnectUrl(&http_request->url, at, length);
    }
    else{
      http_parser_url u;
      int result = http_parser_parse_url(at, length, 0, &u);
      if (result != 0){
        std::cout << "Parser Error " << result << std::endl;
        return result;
      }
      CopyUrlToHttpUrl(&http_request->url, at, length, &u);
    }
    return 0;
  }

  int OnStatus(http_parser* parser, const char *at, size_t length){
    std::cout << __FUNCTION__ << "\t\t";
    std::cout.write(at, length) << std::endl;
    return 0;
  }

  int OnHeaderField(http_parser* parser, const char *at, size_t length){
    proxy::HttpRequest *http_request = (proxy::HttpRequest *)parser->data;
    std::cout << __FUNCTION__ << "\t\t";
    std::cout.write(at, length) << std::endl;

    for (int i = 0; i < SIZEOF_REMVOE_REQUEST_HEADERS; i++){
      if (CaseStrncmp(at, REMOVE_REQUEST_HEADERS[i], length) == 0){
        return 0;
      }
    }
    proxy::HttpHeader header;
    header.name.append(at, length);
    if (CaseStrncmp(at, CONNECTION, length) == 0){
      header.value = CONNECTION_CLOSE;
    }
    http_request->headers.push_back(header);
    return 0;
  }

  int OnHeaderValue(http_parser* parser, const char *at, size_t length){
    proxy::HttpRequest *http_request = (proxy::HttpRequest *)parser->data;
    std::cout << __FUNCTION__ << "\t\t";
    std::cout.write(at, length) << std::endl;

    if (http_request->headers.size() == 0){
      return 0;
    }

    proxy::HttpHeader & head = http_request->headers.back();
    
    if (!head.value.empty()){
      return 0;
    }
    else if (head.name == CONNECTION){
      head.value = CONNECTION_CLOSE;
    }
    else if (head.name == PROXY_CONNECTION){
      head.name = CONNECTION;
      head.value = CONNECTION_CLOSE;
    }
    else{
      head.value.append(at, length);
      std::cout << "\t" << head.name << "\t" << head.value << std::endl;
    }
    return 0;
  }

  int OnHeadersComplete(http_parser* parser){
    proxy::HttpRequest *http_request = (proxy::HttpRequest *)parser->data;
    std::cout << __FUNCTION__ << std::endl;
    http_request->is_header_complete = true;
    return 1;
  }

  int OnBody(http_parser* parser, const char *at, size_t length){
    std::cout << __FUNCTION__ << "\t\t";
    std::cout.write(at, length) << std::endl;
    return 0;
  }

  int OnMessageComplate(http_parser* parser){
    std::cout << __FUNCTION__ << std::endl;
    return 1;
  }

  int OnChunkHeader(http_parser* parser){
    std::cout << __FUNCTION__ << std::endl;
    return 0;
  }

  int OnChunkComplete(http_parser* parser){
    std::cout << __FUNCTION__ << std::endl;
    return 0;
  }

  // ---------------------------------------------------------------------------
  HttpRequestParser::HttpRequestParser()
    :state_(PARSER_STATE_PARSERING){
    InitRequestParser();
  }

  HttpRequestParser::~HttpRequestParser(){

  }

  void HttpRequestParser::InitRequestParser(){
    // Init settings
    settings_.on_body = OnBody;
    settings_.on_chunk_complete = OnChunkComplete;
    settings_.on_chunk_header = OnChunkHeader;
    settings_.on_headers_complete = OnHeadersComplete;
    settings_.on_header_field = OnHeaderField;
    settings_.on_header_value = OnHeaderValue;
    settings_.on_message_begin = OnMessageBegin;
    settings_.on_message_complete = OnMessageComplate;
    settings_.on_status = OnStatus;
    settings_.on_url = OnUrl;
    // Init http_parser
    parser_.data = (void *)&http_request_;
    http_parser_init(&parser_, HTTP_REQUEST);
    // 
    http_request_.is_header_complete = false;
  }

  std::size_t HttpRequestParser::Parse(const char* data, int size){
    int need = http_parser_execute(&parser_, &settings_, data, size);
    if (parser_.http_errno){
      state_ = PARSER_STATE_ERROR;
    }
    if (http_request_.is_header_complete){
      state_ = PARSER_STATE_COMPLETE;
    }
    return need;
  }

  std::string HttpRequestParser::error_message(){
    return http_errno_name((http_errno)parser_.http_errno);
  }

  ParserState HttpRequestParser::State(){
    return state_;
  }
} // namespace proxy